﻿using UnityEngine;

public delegate void FSMDelegate<TStateName>(TStateName oldStateName, TStateName newStateName) where TStateName : System.Enum;
/// <summary>
/// 有限状态机基类，继承MonoBehaviour，需要在Update中对状态进行初始化
/// 尽量别重写Update，有需要的话可以使用OnUpdate
/// </summary>
/// <typeparam name="TStateName">枚举状态类，必须是枚举</typeparam>
/// <typeparam name="TState">状态基类，状态基类都继承IState</typeparam>
public abstract class FSM<TStateName, TState> : MonoBehaviour where TStateName : System.Enum where TState : IState {

    /// <summary>
    /// 当前状态枚举
    /// </summary>
    //[HideInInspector]
    public TStateName currStateName;

    /// <summary>
    /// 当状态发生变化时调用，<oldStateName, newStateName>
    /// </summary>
    /// public event Action<TStateName, TStateName> OnStateChange;

    /// <summary>
    /// 应该放类外部的，但是就不
    /// </summary>
    /// public delegate void FSMDelegate<T>(T oldStateName, T newStateName) where T : System.Enum;

    /// <summary>
    /// 当状态发生变化时调用，<oldStateName, newStateName>
    /// </summary>
    public event FSMDelegate<TStateName> OnStateChange;

    /// <summary>
    /// 当前状态
    /// </summary>
    protected TState currState;

    /// <summary>
    /// 状态键值对（状态枚举，状态类）
    /// </summary>
    protected System.Collections.Generic.Dictionary<TStateName, TState> states;

    /// <summary>
    /// 不能重新Update，不然该处会被覆盖
    /// </summary>
    private void Update() {
        OnUpdate();
    }

    protected virtual void OnUpdate() {
        currState?.OnUpdate();
    }

    /// <summary>
    /// 切换状态
    /// </summary>
    /// <param name="stateName">要切换到的状态枚举</param>
    /// <returns>返回是否切换成功</returns>
    public virtual bool ChangeState(TStateName stateName) {
        if (states.TryGetValue(stateName, out TState state)) {
            currState?.OnExit();
            OnStateChange?.Invoke(currStateName, stateName);
            currStateName = stateName;
            currState = state;
            currState.OnEnter();
            return true;
        } else {
            Debug.LogError($"不存在状态--{stateName}--无法切换。");
            return false;
        }
    }

}

public interface IState {
    /// <summary>
    /// 进入状态时候调用
    /// </summary>
    void OnEnter();
    /// <summary>
    /// 在状态中每帧调用
    /// </summary>
    void OnUpdate();
    /// <summary>
    /// 退出状态时候调用
    /// </summary>
    void OnExit();
}

